import { app } from "/scripts/app.js";
import { api } from "/scripts/api.js";

const PADDING = 8;
const HEADER_HEIGHT = 460; 

/**
 * Parses a comma-separated RGB or RGBA string into a valid CSS rgba() color.
 * @param {string} colorString The input string, e.g., "255,0,0" or "255,0,0,128".
 * @returns {string} A CSS rgba() color string.
 */
function parseColorString(colorString) {
    const defaultColor = { r: 192, g: 192, b: 192, a: 1.0 };
    
    if (typeof colorString !== 'string') {
        return `rgba(${defaultColor.r}, ${defaultColor.g}, ${defaultColor.b}, ${defaultColor.a})`;
    }

    const parts = colorString.split(',').map(s => parseInt(s.trim(), 10));

    const r = !isNaN(parts[0]) ? parts[0] : defaultColor.r;
    const g = !isNaN(parts[1]) ? parts[1] : defaultColor.g;
    const b = !isNaN(parts[2]) ? parts[2] : defaultColor.b;
    const a = (parts.length > 3 && !isNaN(parts[3])) ? parts[3] / 255.0 : 1.0;

    return `rgba(${r}, ${g}, ${b}, ${a})`;
}



// Helper function to draw a quarter-circle 
// arc inside a given square.
function drawArcInSquare({ ctx, square, compass }) {
    ctx.beginPath();
    const { x, y, side } = square;
    const PI = Math.PI;
    
    switch (compass) {
        case 'right':
            ctx.arc(x + side, y, side, PI / 2, PI);
            break;
        case 'down':
            ctx.arc(x + side, y + side, side, PI, 3 * PI / 2);
            break;
        case 'left':
            ctx.arc(x, y + side, side, 3 * PI / 2, 2 * PI);
            break;
        case 'up':
            ctx.arc(x, y, side, 0, PI / 2);
            break;
    }
    ctx.stroke();
}


// Draws the golden spiral, with all features: 
// scaling, cropping, and flipping.
// Implements stretch-from-center for axial stretch.
function drawGoldenSquareDivision({
    ctx,
    canvasWidth,
    canvasHeight,
    initialCompass,
    lineWidth,
    uniformLineWidth,
    globalScale,
    axialStretch,
    color,
    steps,
    drawSpiral = false,
    mode = 'scale',
    cropOffset = 0.5,
    flipX = false,
    flipY = false
}) {
    const PHI = 1.61803398875;

    ctx.save();

    const isVertical = initialCompass === 'up' || initialCompass === 'down';

    // Center the Axial Stretch ---

    // 1. Move origin to the center of the drawing area.
    ctx.translate(canvasWidth / 2, canvasHeight / 2);

    // 2. Apply the stretch from the new, centered origin.
    if (isVertical) {
        ctx.scale(1, axialStretch);
    } 
    else {
        ctx.scale(axialStretch, 1);
    }


    // 3. Move the origin back.

    ctx.translate(-canvasWidth / 2, -canvasHeight / 2);
    
    // --- END of new logic ---



    // Apply final screen-space flips
    if (flipX) {
        ctx.translate(canvasWidth, 0);
        ctx.scale(-1, 1);
    }
    
    if (flipY) {
        ctx.translate(0, canvasHeight);
        ctx.scale(1, -1);
    }

    let logicalWidth, logicalHeight;

    let effectiveLineWidth = lineWidth;
    
    if (uniformLineWidth) {
        effectiveLineWidth = lineWidth / globalScale;
    }

    // Apply crop or scale-to-fit logic
    if (mode === 'crop') {
        
        if (isVertical) {
            logicalWidth = canvasWidth;
            logicalHeight = canvasWidth * PHI;
        } 
        else {
            logicalHeight = canvasHeight;
            logicalWidth = canvasHeight * PHI;
        }

        const deltaX = canvasWidth - logicalWidth;
        const deltaY = canvasHeight - logicalHeight;
        const translateX = isVertical ? deltaX / 2 : deltaX * (1 - cropOffset);
        const translateY = isVertical ? deltaY * (1 - cropOffset) : deltaY / 2;
        ctx.translate(translateX, translateY);
        ctx.lineWidth = effectiveLineWidth;

    } 
    else {
        // 'scale' mode (stretch-to-fit)
        logicalWidth = canvasWidth;
        logicalHeight = canvasHeight;
        let scaleX = 1.0, scaleY = 1.0;

        if (isVertical) {
            const idealHeight = canvasWidth * PHI;
            if (idealHeight > 0) scaleY = canvasHeight / idealHeight;
            
            logicalHeight = idealHeight;
        } 
        else {
            const idealWidth = canvasHeight * PHI;
            if (idealWidth > 0) scaleX = canvasWidth / idealWidth;
            
            logicalWidth = idealWidth;
        }

        ctx.scale(scaleX, scaleY);
        ctx.lineWidth = effectiveLineWidth / Math.max(scaleX, scaleY, 1);
    }

    let rect = { x: 0, y: 0, width: logicalWidth, height: logicalHeight };
    
    // Sets the new color (move to keep single color)
    ctx.strokeStyle = color; 
    
    // Draws the box with the new color
    ctx.strokeRect(0, 0, logicalWidth, logicalHeight); 
    let compass = initialCompass;

    // The core drawing logic
    for (let i = 0; i < steps; i++) {
        if (rect.width < 1 || rect.height < 1) break;
        
        let carvedSquare;
        ctx.beginPath();
        
        switch (compass) {
            case 'up':
                carvedSquare = { x: rect.x, y: rect.y + (rect.height - rect.width), side: rect.width };
                ctx.moveTo(carvedSquare.x, carvedSquare.y);
                ctx.lineTo(carvedSquare.x + carvedSquare.side, carvedSquare.y);
                rect.height -= carvedSquare.side;
                compass = 'right';
                break;
            case 'right':
                carvedSquare = { x: rect.x, y: rect.y, side: rect.height };
                ctx.moveTo(carvedSquare.x + carvedSquare.side, carvedSquare.y);
                ctx.lineTo(carvedSquare.x + carvedSquare.side, carvedSquare.y + carvedSquare.side);
                rect.x += carvedSquare.side;
                rect.width -= carvedSquare.side;
                compass = 'down';
                break;
            case 'down':
                carvedSquare = { x: rect.x, y: rect.y, side: rect.width };
                ctx.moveTo(carvedSquare.x, carvedSquare.y + carvedSquare.side);
                ctx.lineTo(carvedSquare.x + carvedSquare.side, carvedSquare.y + carvedSquare.side);
                rect.y += carvedSquare.side;
                rect.height -= carvedSquare.side;
                compass = 'left';
                break;
            case 'left':
                carvedSquare = { x: rect.x + (rect.width - rect.height), y: rect.y, side: rect.height };
                ctx.moveTo(carvedSquare.x, carvedSquare.y);
                ctx.lineTo(carvedSquare.x, carvedSquare.y + carvedSquare.side);
                rect.width -= carvedSquare.side;
                compass = 'up';
                break;
        }

        ctx.stroke();
        
        if (drawSpiral && carvedSquare) {
            drawArcInSquare({ ctx, square: carvedSquare, compass: compass });
        }
    }

    ctx.restore();
}


app.registerExtension({
    name: "Eses.CompositionGoldenRatio",
    
    nodeCreated(node) {
        if (node.comfyClass === "EsesCompositionGoldenRatio") {
            node.imagePreview = null;
            node.isManuallyResized = false;
            node.size = [320, 520];

            const getDrawArea = () => {
                const area = {
                    x: PADDING,
                    y: HEADER_HEIGHT,
                    width: node.size[0] - PADDING * 2,
                    height: node.size[1] - HEADER_HEIGHT - PADDING
                };
                return (area.width < 1 || area.height < 1) ? null : area;
            };

            node.onResize = function() {
                this.isManuallyResized = true;
            };


            node.onDrawForeground = function(ctx) {

                // Check if the node is collapsed - use flags property
                if (this.flags && this.flags.collapsed) {
                    return;
                }

                const drawArea = getDrawArea();
                if (!drawArea) return;


                if (this.imagePreview) {
                    const imageRatio = this.imagePreview.width / this.imagePreview.height;
                    const containerRatio = drawArea.width / drawArea.height;
                    let drawWidth = drawArea.width, drawHeight = drawArea.height;
                    if (imageRatio > containerRatio) {
                        drawHeight = drawArea.width / imageRatio;
                    } else {
                        drawWidth = drawArea.height * imageRatio;
                    }
                    const offsetX = drawArea.x + (drawArea.width - drawWidth) / 2;
                    const offsetY = drawArea.y + (drawArea.height - drawHeight) / 2;
                    
                    ctx.drawImage(this.imagePreview, offsetX, offsetY, drawWidth, drawHeight);
                    
                    ctx.save();
                    
                    ctx.beginPath();
                    ctx.rect(offsetX, offsetY, drawWidth, drawHeight);
                    ctx.clip();
                    
                    const widgets = Object.fromEntries(node.widgets.map(w => [w.name, w.value]));
                    
                    // Note: 
                    // The drawing function sets its own 
                    // strokeStyle and lineWidth.
                    // These values are passed into it.
                    ctx.globalCompositeOperation = widgets.blend_mode || "source-over";
                    
                    const PHI = 1.61803398875;
                    let effectiveCompass = widgets.orientation;
                    
                    // Handle 'auto' orientation 
                    // based on image aspect ratio
                    if (effectiveCompass === 'auto') {
                        effectiveCompass = (this.imagePreview.width >= this.imagePreview.height) ? 'right' : 'up';
                    }
                    
                    // Console log for automatic cropping
                    const imageAspectRatio = drawWidth / drawHeight;
                    const isVertical = effectiveCompass === 'up' || effectiveCompass === 'down';
                    const targetAspectRatio = isVertical ? (1 / PHI) : PHI;
                    
                    if (widgets.fit_mode === 'crop' && Math.abs(imageAspectRatio - targetAspectRatio) > 0.02) {
                        console.log(`Eses Golden Ratio: Image aspect ratio (${imageAspectRatio.toFixed(2)}) is not a golden ratio. Using 'crop' mode.`);
                    }
                    

                    // --- Apply Projection Transforms ---

                    // Move the "origin" to the center of the 
                    // image preview area for more intuitive transforms.
                    ctx.translate(offsetX + drawWidth / 2, offsetY + drawHeight / 2);

                    // Apply user-controlled transforms from the widgets
                    ctx.translate(widgets.offset_x, widgets.offset_y);
                    ctx.rotate(widgets.rotation * Math.PI / 180); // Convert degrees to radians
                    ctx.scale(widgets.scale, widgets.scale);

                    // Translate back by half the dimensions so the 
                    // pattern draws centered on the new origin
                    ctx.translate(-drawWidth / 2, -drawHeight / 2);

                    drawGoldenSquareDivision({
                        ctx: ctx,
                        canvasWidth: drawWidth,
                        canvasHeight: drawHeight,
                        initialCompass: effectiveCompass,
                        lineWidth: widgets.line_thickness,
                        uniformLineWidth: widgets.uniform_line_width,
                        globalScale: widgets.scale,
                        axialStretch: widgets.axial_stretch,
                        color: parseColorString(widgets.line_color_rgb),
                        steps: widgets.steps,
                        drawSpiral: widgets.draw_spiral,
                        mode: widgets.fit_mode === 'stretch' ? 'scale' : 'crop',
                        cropOffset: widgets.crop_offset,
                        flipX: widgets.flip_horizontal,
                        flipY: widgets.flip_vertical
                    });

                    
                    ctx.restore();

                    ctx.strokeStyle = "rgba(40, 40, 40, 1)";
                    ctx.lineWidth = 1;
                    ctx.strokeRect(offsetX, offsetY, drawWidth, drawHeight);

                } 
                else { 
                    ctx.save();
                    ctx.font = "14px Arial";
                    ctx.fillStyle = "#CCCCCC";
                    ctx.textAlign = "center";
                    ctx.textBaseline = "middle";
                    ctx.fillText("Connect Image and run workflow", drawArea.x + drawArea.width / 2, drawArea.y + drawArea.height / 2);
                    ctx.restore();
                }
            };

            
            const widgetsToMonitor = [
                "preview_resolution_limit", "line_color_rgb", "line_thickness", "uniform_line_width", "blend_mode",
                "orientation", "fit_mode", "crop_offset", "flip_horizontal", "flip_vertical", "draw_spiral", "steps", "axial_stretch",
                "offset_x", "offset_y", "rotation", "scale"
            ];

            for (const w of node.widgets) {
                if (widgetsToMonitor.includes(w.name)) {
                    w.callback = () => node.setDirtyCanvas(true, true);
                }
            }
            
        }
    },
});


// Listener ----------

api.addEventListener("eses.composition_golden_ratio_preview", ({ detail }) => {
    const node = app.graph.getNodeById(detail.node_id);
    if (node) {
        const img = new Image();
        img.src = `data:image/png;base64,${detail.image_data}`;
        
        img.onload = () => {
            node.imagePreview = img;
            if (!node.isManuallyResized) {
                const aspectRatio = img.naturalWidth / img.naturalHeight;
                const baseWidth = node.size[0];
                const previewAreaHeight = (baseWidth - (PADDING * 2)) / aspectRatio;
                node.size[1] = HEADER_HEIGHT + previewAreaHeight + PADDING;
            }
            app.graph.setDirtyCanvas(true, true);
        };
    }
});
